# coding: utf-8
from typing import Union

from qfluentwidgets import TransparentToolButton, FluentIcon as FIF, ToolTipFilter, ToolTipPosition, isDarkTheme, \
    InfoBar
from qtpy.QtCore import Qt, QRectF, QSize, Signal, QPropertyAnimation, QEasingCurve, Property
from qtpy.QtGui import QPainter, QPixmap, QWheelEvent, QResizeEvent, QImage, QColor
from qtpy.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsItem, QWidget, \
    QHBoxLayout, QFileDialog, QVBoxLayout

from ....common import HTTPRequest, NetworkResponse


class PictureToolButton(TransparentToolButton):
    def _postInit(self):
        self.setFixedSize(30, 30)
        self.installEventFilter(ToolTipFilter(self, position=ToolTipPosition.BOTTOM))


class PictureToolWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.horizontalLayout = QHBoxLayout(self)
        self.rotateBtn = PictureToolButton(FIF.ROTATE, self)
        self.enlargeBtn = PictureToolButton(FIF.ZOOM_IN, self)
        self.shrinkBtn = PictureToolButton(FIF.ZOOM_OUT, self)
        self.fullScreenBtn = PictureToolButton(FIF.FULL_SCREEN, self)
        self.saveBtn = PictureToolButton(FIF.SAVE, self)
        self.closeBtn = PictureToolButton(FIF.CLOSE, self)
        self.__initWidgets()

    def __initWidgets(self):
        self.rotateBtn.setToolTip(self.tr("旋转图片"))
        self.enlargeBtn.setToolTip(self.tr("放大图片"))
        self.shrinkBtn.setToolTip(self.tr("缩小图片"))
        self.fullScreenBtn.setToolTip(self.tr("全屏显示"))
        self.saveBtn.setToolTip(self.tr("保存图片"))
        self.closeBtn.setToolTip(self.tr("关闭窗口"))

        self.horizontalLayout.setAlignment(Qt.AlignRight)
        self.horizontalLayout.setSpacing(6)
        self.horizontalLayout.setContentsMargins(5, 5, 5, 5)
        self.horizontalLayout.addStretch(1)
        self.horizontalLayout.addWidget(self.rotateBtn)
        self.horizontalLayout.addWidget(self.enlargeBtn)
        self.horizontalLayout.addWidget(self.shrinkBtn)
        self.horizontalLayout.addWidget(self.fullScreenBtn)
        self.horizontalLayout.addWidget(self.saveBtn)
        self.horizontalLayout.addWidget(self.closeBtn)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(Qt.NoPen)
        c = 0 if isDarkTheme() else 255
        painter.setBrush(QColor(c, c, c, 100))
        painter.drawRoundedRect(self.rect(), 5, 5)
        painter.end()


class PictureBrowserView(QGraphicsView):
    """
    图片查看器
    """
    closeSignal = Signal(bool)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._rotationAngle = 0.0
        self.zoomInTimes = 0
        self.maxZoomInTimes = 22
        self.displayedImageSize = QSize(0, 0)

        self.verticalLayout = QVBoxLayout(self)
        self.toolsBar = PictureToolWidget(self)
        self.graphicsScene = QGraphicsScene(self)
        self.pixmapItem = QGraphicsPixmapItem()

        self.__animation = QPropertyAnimation(self)
        self.__animation.setTargetObject(self)
        self.__animation.setPropertyName(b'rotation')
        self.__animation.setDuration(100)
        self.__animation.setEasingCurve(QEasingCurve.InOutQuart)

        self.__initWidgets()
        self.__initSignals()

    def __initWidgets(self):
        self.toolsBar.move(0, 0)
        self.toolsBar.resize(self.width(), 50)

        self.setAlignment(Qt.AlignCenter)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)  # 隐藏水平滚动条
        self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)  # 隐藏垂直滚动条
        self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)  # 以鼠标所在位置为锚点进行缩放
        self.pixmapItem.setTransformationMode(Qt.TransformationMode.SmoothTransformation)  # 平滑转型
        self.setRenderHints(
            QPainter.HighQualityAntialiasing | QPainter.LosslessImageRendering | QPainter.SmoothPixmapTransform)  # 平滑像素图变换
        self.setContentsMargins(0, 0, 0, 0)
        self.setViewportMargins(0, 0, 0, 0)
        # 设置布局
        self.verticalLayout.setContentsMargins(10, 10, 10, 10)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.addWidget(self.toolsBar, 0, Qt.AlignTop | Qt.AlignRight)
        # 添加图片标签
        self.graphicsScene.addItem(self.pixmapItem)
        self.setScene(self.graphicsScene)
        # 设置控件样式
        self.setStyleSheet(
            'QGraphicsView{background-color: transparent; border: none;}'
        )

    def __initSignals(self):
        self.toolsBar.rotateBtn.clicked.connect(lambda: self.setRotationByAnimation())
        self.toolsBar.enlargeBtn.clicked.connect(lambda: self.enlargePicture())
        self.toolsBar.shrinkBtn.clicked.connect(lambda: self.shrinkPicture())
        self.toolsBar.fullScreenBtn.clicked.connect(self.__windowFullOrNormal)
        self.toolsBar.saveBtn.clicked.connect(self.__saveImage)
        self.toolsBar.closeBtn.clicked.connect(self.closeSignal)

    def __windowFullOrNormal(self):
        if self.isFullScreen():
            self.setWindowFlags(Qt.SubWindow)
            self.toolsBar.fullScreenBtn.setIcon(FIF.FULL_SCREEN)
            self.showNormal()
        else:
            self.setWindowFlags(Qt.Window)
            self.toolsBar.fullScreenBtn.setIcon(FIF.BACK_TO_WINDOW)
            self.showFullScreen()

    def __saveImage(self):
        fileName, t = QFileDialog.getSaveFileName(self, self.tr("保存图片"), "", self.tr("图片 (*.png *.jpg *.jpeg)"))
        if fileName:
            self.pixmapItem.pixmap().save(fileName)
            InfoBar.success('', self.tr("图片已保存到") + fileName, self.window())

    def __getScaleRatio(self):
        """
        获取显示的图像和原始图像的缩放比例
        :return:
        """
        pm = self.pixmapItem.pixmap()
        if pm.isNull():
            return 1

        pw = pm.width()
        ph = pm.height()
        rw = min(1, self.width() / pw)
        rh = min(1, self.height() / ph)
        return min(rw, rh)

    def __setDragEnabled(self, isEnabled: bool):
        """
        设置拖拽是否启动
        :param isEnabled: bool
        :return:
        """
        if isEnabled:
            self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
        else:
            self.setDragMode(QGraphicsView.DragMode.NoDrag)

    def __isEnableDrag(self):
        """
        根据图片的尺寸决定是否启动拖拽功能
        :return:
        """
        v = self.verticalScrollBar().maximum() > 0
        h = self.horizontalScrollBar().maximum() > 0
        return v or h

    def getRotation(self) -> float:
        return self.pixmapItem.rotation()

    def setRotation(self, stepSize: Union[float, int] = 90.0):
        """
        顺时针旋转
        :param stepSize: 步长，旋转角度
        :return:
        """
        if self.pixmapItem.pixmap().isNull():
            return
        # self.pixmapItem.setTransformOriginPoint(self.pixmapItem.boundingRect().center())  # 指定图片旋转中心点
        self.pixmapItem.setRotation(stepSize)

    def setRotationByAnimation(self, stepSize: int = 90):
        """
        顺时针旋转动画
        :param stepSize: 步长，旋转角度
        :return:
        """
        if self.__animation.state() == QPropertyAnimation.State.Running:
            return
        self.__animation.setStartValue(self._rotationAngle)
        self._rotationAngle += stepSize
        self.__animation.setEndValue(self._rotationAngle)
        self.__animation.start()

    def resetTransform(self):
        """
        重置变换
        :return:
        """
        self.zoomInTimes = 0
        self.__setDragEnabled(False)
        super().resetTransform()

    def setAdaptation(self):
        """
        缩放以适应
        :return:
        """
        self.setSceneRect(QRectF(self.pixmapItem.pixmap().rect()))
        self.fitInView(self.pixmapItem)
        self.__setDragEnabled(False)
        self.zoomInTimes = 0

    def setOriginalSize(self):
        """
        设置 1:1 大小
        :return:
        """
        self.resetTransform()
        self.setSceneRect(QRectF(self.pixmapItem.pixmap().rect()))
        self.__setDragEnabled(self.__isEnableDrag())
        self.zoomInTimes = self.getZoomInTimes(self.pixmapItem.pixmap().width())

    def enlargePicture(self, anchor=QGraphicsView.AnchorUnderMouse):
        """
        放大图片
        :return:
        """
        if self.zoomInTimes == self.maxZoomInTimes:
            return
        self.setTransformationAnchor(anchor)
        self.zoomInTimes += 1
        self.scale(1.1, 1.1)
        self.__setDragEnabled(self.__isEnableDrag())

        # 还原 anchor
        self.setTransformationAnchor(self.AnchorUnderMouse)

    def shrinkPicture(self, anchor=QGraphicsView.AnchorUnderMouse):
        """
        缩小图片
        :return:
        """
        if self.zoomInTimes == 0 and not self.__isEnableDrag():
            return

        self.setTransformationAnchor(anchor)

        self.zoomInTimes -= 1

        # 原始图像的大小
        pm = self.pixmapItem.pixmap()
        pw = pm.width()
        ph = pm.height()

        # 实际显示的图像宽度
        w = self.displayedImageSize.width() * 1.1 ** self.zoomInTimes
        h = self.displayedImageSize.height() * 1.1 ** self.zoomInTimes

        if pw > self.width() or ph > self.height():
            # 在窗口尺寸小于原始图像时禁止继续缩小图像比窗口还小
            if w <= self.width() and h <= self.height():
                self.fitInView(self.pixmapItem)
            else:
                self.scale(1 / 1.1, 1 / 1.1)
        else:
            # 在窗口尺寸大于图像时不允许缩小的比原始图像小
            if w <= pw:
                self.resetTransform()
            else:
                self.scale(1 / 1.1, 1 / 1.1)

        self.__setDragEnabled(self.__isEnableDrag())

        # 还原 anchor
        self.setTransformationAnchor(self.AnchorUnderMouse)

    def getZoomInTimes(self, width: int, step: int = 100):
        for i in range(0, self.maxZoomInTimes):
            if width - self.displayedImageSize.width() * 1.1 ** i <= step:
                return i
        return self.maxZoomInTimes

    def fitInView(self, item: QGraphicsItem, mode=Qt.AspectRatioMode.KeepAspectRatio):
        """
        缩放场景使其适应窗口大小
        :param item:
        :param mode:
        :return:
        """
        super().fitInView(item, mode)
        self.displayedImageSize = self.__getScaleRatio() * self.pixmapItem.pixmap().size()
        self.zoomInTimes = 0

    def resizeEvent(self, event: QResizeEvent):
        """
        重写 resizeEvent 事件，调整图片大小
        :param event:
        :return:
        """
        if self.zoomInTimes > 0:
            return
        # 调整图片大小
        ratio = self.__getScaleRatio()
        self.displayedImageSize = self.pixmapItem.pixmap().size() * ratio
        if ratio < 1:
            self.fitInView(self.pixmapItem)
        else:
            self.resetTransform()
        super().resizeEvent(event)

    def resetTransform(self):
        """
        重置变换
        :return:
        """
        self.zoomInTimes = 0
        self.__setDragEnabled(False)
        super().resetTransform()

    def wheelEvent(self, e: QWheelEvent):
        """
        滚动鼠标滚轮缩放图片
        :param e:
        :return:
        """
        if e.angleDelta().y() > 0:
            self.enlargePicture()
        else:
            self.shrinkPicture()

    def setCloseVisible(self, visible: bool):
        self.toolsBar.closeBtn.setVisible(visible)

    def setPixmap(self, pixmap: Union[str, QPixmap, QImage]):
        """
        设置图片
        :param pixmap:
        :return:
        """
        if not pixmap:
            return
        if isinstance(pixmap, str):
            pixmap = QPixmap(pixmap)
        elif isinstance(pixmap, QImage):
            pixmap = QPixmap.fromImage(pixmap)
        # 设置图片, 并设置场景大小
        self.pixmapItem.setPixmap(pixmap)
        # 缩放图片
        ratio = self.__getScaleRatio()
        self.displayedImageSize = pixmap.size() * ratio
        self.pixmapItem.setTransformOriginPoint(self.pixmapItem.boundingRect().center())
        self.resetTransform()  # 重置变换

    def setUrl(self, url: str, headers: dict = None):
        HTTPRequest.get(url, headers=headers, response_callback=self.__onRequestFinished, parent=self)

    def __onRequestFinished(self, response: NetworkResponse):
        print(response.status_code)
        self.setPixmap(response.pixmap(FIF.GITHUB.value))
        response.deleteLater()

    rotation = Property(float, getRotation, setRotation)
